<?php
// 策略模式
// 策略模式是指对一系列的算法定义，并将每一个算法封装起来，而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。
// 策略模式的优点有：策略模式提供了管理相关的算法族的办法、策略模式提供了可以替换继承关系的办法、使用策略模式可以避免使用多重条件转移语句。

// 策略模式实现步骤
// —抽象策略角色： 策略类，通常由一个接口或者抽象类实现。
// —具体策略角色：包装了相关的算法和行为。
// —环境角色：持有一个策略类的引用，最终给客户端调用。


// 在软件开发中也常常遇到类似的情况，实现某一个功能有多种算法或者策略，我们可以根据环境或者条件的不同选择不同的算法或者策略来完成该功能。
// 如查找、排序等，一种常用的方法是硬编码（Hard Coding）在一个类中，如需要提供多种查找算法，可以将这些算法写到一个类中，在该类中提供多个方法，
// 每一个方法对应一个具体的查找算法；当然也可以将这些查找算法封装在一个统一的方法中，通过if…else…或者case等条件判断语句来进行选择。
// 这两种实现方法我们都可以称之为硬编码，如果需要增加一种新的查找算法，需要修改封装算法类的源代码；更换查找算法，也需要修改客户端调用代码。
// 在这个算法类中封装了大量查找算法，该类代码将较复杂，维护较为困难。如果我们将这些策略包含在客户端，这种做法更不可取，
// 将导致客户端程序庞大而且难以维护，如果存在大量可供选择的算法时问题将变得更加严重。
// 如何让算法和对象分开来，使得算法可以独立于使用它的客户而变化？为此我们引入策略模式。
// 策略模式（Strategy），又叫算法簇模式，就是定义了不同的算法族，并且之间可以互相替换，此模式让算法的变化独立于使用算法的客户。
// 常见的使用场景比如对象筛选，可以根据日期筛选，也可以根据 ID 筛选；又比如在单元测试中，我们可以在文件和内存存储之间进行切换。


// 分析(假如目前还没有计算器，我们要制造一个计算器)
// 我们知道计算器的共性是拥有计算结果功能
// 而计算结果的功能又可以分为计算加、计算减..
// 因此我们可以将加、减这些计算各自实现成各自的计算策略
// 在我们使用计算器的时候，我们只要知道是进行的什么操作
// 然后调用具体的计算策略即可

// 抽象计算器计算策略

/**
 * Class Calculator
 */
abstract class Calculator
{
    /**
     * calc
     * @param $a
     * @param $b
     * @return mixed
     */
    abstract public function calc($a, $b);
}

//加法计算策略

/**
 * Class Add
 */
class Add extends Calculator
{
    /**
     * calc
     * @param $a
     * @param $b
     * @return mixed
     */
    public function calc($a, $b)
    {
        return $a + $b;
    }
}

//减法计算策略

/**
 * Class Sub
 */
class Sub extends Calculator
{
    /**
     * calc
     * @param $a
     * @param $b
     * @return mixed
     */
    public function calc($a, $b)
    {
        return $a - $b;
    }
}

//乘法计算策略

/**
 * Class Mult
 */
class Mult extends Calculator
{
    /**
     * calc
     * @param $a
     * @param $b
     * @return mixed
     */
    public function calc($a, $b)
    {
        return $a * $b;
    }
}

//除法法计算策略

/**
 * Class Div
 */
class Div extends Calculator
{
    /**
     * calc
     * @param $a
     * @param $b
     * @return float|int
     */
    public function calc($a, $b)
    {
        return $a / $b;
    }
}


/**
 * 运用环境
 * Class Handle
 */
class Handle
{
    /**
     * @var Calculator
     */
    protected $calculator;
    
    /**
     * Handle constructor.
     * @param \Calculator $handle
     */
    public function __construct(Calculator $handle)
    {
        $this->calculator = $handle;
    }
    
    /**
     * calc
     * @param $a
     * @param $b
     * @return mixed
     */
    public function calc($a, $b)
    {
        return $this->calculator->calc($a, $b);
    }
}

$obj = new Handle(new Div());
echo $obj->calc(1, 2);


/*** 策略模式具体运用 ***/
// 假设有一个内容网站，分为视频内容、音频内容...
// 下面我们进行不同站点的展示策略制作

Interface WebsiteShow
{
    public function theme();
    
    public function ranking();
}

class VideoShow implements WebsiteShow
{
    
    public function theme()
    {
        return '视频主题';
    }
    
    public function ranking()
    {
        return [
            '猎场',
            '伪装者'
        ];
    }
}

class Music implements WebsiteShow
{
    
    public function theme()
    {
        return '音乐主题';
    }
    
    public function ranking()
    {
        return [
            '一眼万年',
            '十年'
        ];
    }
}

class IndexController
{
    private $websiteShow;
    
    /**
     * IndexController constructor.
     * @param WebsiteShow $object
     */
    public function __construct(WebsiteShow $object)
    {
        $this->websiteShow = $object;
    }
    
    public function index()
    {
        $theme = $this->websiteShow->theme();
        $rank  = $this->websiteShow->ranking();
        echo '主题:' . PHP_EOL;
        echo $theme. PHP_EOL;
        echo '首页推荐排行:' . PHP_EOL;
        var_dump($rank);
    }
}

$website = $_GET['wevsite'] ?? 'video';
switch ($website) {
    case 'music':
        $object = new Music();
        break;
    case 'video':
    default:
        $object = new VideoShow();
        break;
}
$controller = new IndexController($object);
$controller->index();


/**
 * 声明一个主题算法接口
 * Interface Theme
 */
Interface Theme
{
    public function employ();
}

/**
 * 设计师创建了一套未来的主题
 * Class FutureTheme
 */
class FutureTheme implements Theme
{
    /**
     * employ 应用主题
     * @return string
     */
    public function employ()
    {
        return 'this is FutureTheme';
    }
}

/**
 * 设计师创建了一套拟物化的主题
 * Class MaterializationTheme
 */
class MaterializationTheme implements Theme
{
    /**
     * employ 应用主题
     * @return string
     */
    public function employ()
    {
        return 'this is MaterializationTheme';
    }
}

/**
 * 设计师创建了一套扁平化的主题
 * Class FlatTheme
 */
class FlatTheme implements Theme
{
    /**
     * employ 应用主题
     * @return string
     */
    public function employ()
    {
        return 'this is FlatTheme';
    }
}

/**
 * 设计师创建了一套仿IOS的主题
 * Class IOSTheme
 */
class IOSTheme implements Theme
{
    /**
     * employ 应用主题
     * @return string
     */
    public function employ()
    {
        return 'this is IOSTheme';
    }
}

/**
 * MIUI 桌面
 * Class ThemeDesktop
 */
class ThemeDesktop
{
    /**
     * @var \Theme
     */
    private $theme;
    
    /**
     * IndexController constructor.
     * @param Theme $theme
     */
    public function __construct(Theme $theme)
    {
        $this->theme = $theme;
    }
    
    /**
     * 显示桌面内容
     * display
     */
    public function display()
    {
        echo $this->theme->employ();
    }
}

$website = $_GET['theme'] ?? 'FutureTheme';
switch ($website) {
    case 'MaterializationTheme':
        $object = new MaterializationTheme();
        break;
    case 'FlatTheme':
        $object = new FlatTheme();
        break;
    case 'IOSTheme':
        $object = new IOSTheme();
        break;
    case 'FutureTheme':
    default:
        $object = new FutureTheme();
        break;
}
$controller = new ThemeDesktop($object);
$controller->display();